package com.yuantu.judiciary.serviceimpl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import com.yuantu.judiciary.common.SysCode;
import com.yuantu.judiciary.dao.*;
import com.yuantu.judiciary.exception.ServiceException;
import com.yuantu.judiciary.exception.enums.AccountExceptionEnum;
import com.yuantu.judiciary.exception.enums.DisCountCodeExceptionEnum;
import com.yuantu.judiciary.exception.enums.InviteCodeExceptionEnum;
import com.yuantu.judiciary.model.domain.*;
import com.yuantu.judiciary.model.dto.UserOrderDTO;
import com.yuantu.judiciary.model.enums.*;
import com.yuantu.judiciary.model.form.*;
import com.yuantu.judiciary.model.vo.*;
import com.yuantu.judiciary.service.IInviteCodeService;
import com.yuantu.judiciary.service.IMembershipRankService;
import com.yuantu.judiciary.service.IOperatorService;
import com.yuantu.judiciary.utils.AssertUtil;
import com.yuantu.judiciary.utils.DateUtil;
import com.yuantu.judiciary.utils.ExcelUtil;
import com.yuantu.judiciary.utils.PageUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 运营商管理模块
 *
 * @author syw
 * @since 2023-09-19
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class OperatorServiceImpl implements IOperatorService {

    private final IInviteCodeDao inviteCodeDao;
    private final IInviteCodeService inviteCodeService;
    private final IAccountDao accountDao;
    private final IOrganizationDao organizationDao;
    private final IInviteRecordDao inviteRecordDao;
    private final IVipOrderDao vipOrderDao;
    private final IExtractPointDao extractPointDao;
    private final IUserInfoDao userInfoDao;
    private final IDiscountCodeDao discountCodeDao;
    private final IMembershipRankDao membershipRankDao;
    private final IQueryRecordDao queryRecordDao;
    private final IMembershipRankService membershipRankService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddOperatorForm form) {
        InviteCodeDO inviteCodeDO = new InviteCodeDO();
        inviteCodeDO.setType(InviteCodeType.INVITE);
        inviteCodeDO.setInviteCode(inviteCodeService.getRandom());
        inviteCodeDO.setOperatorId(form.getAccountId());
        inviteCodeDao.save(inviteCodeDO);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Long accountId, List<Long> id) {
        inviteCodeDao.deleteByIdIn(id, accountId, new Timestamp(System.currentTimeMillis()));
        return true;
    }

    @Override
    public PageVO<OperatorVO> list(SearchAccountForm form, Pageable pageable) {
        List<IAccountDao.AccountUserInfo> accountUserInfos = accountDao.searchAccountInfo(form.getPhone(), Arrays.asList(UserType.PRIMARY, UserType.SUB, UserType.NONE));
        if (CollectionUtils.isEmpty(accountUserInfos)) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        Map<Long, IAccountDao.AccountUserInfo> accountMap = accountUserInfos.stream().filter(Objects::nonNull).collect(Collectors.toMap(IAccountDao.AccountUserInfo::getAccountId, v -> v));
        Map<Long, OrganizationDO> organizationMap = new HashMap<>();
        List<Long> organizationIds = accountUserInfos.stream().map(IAccountDao.AccountUserInfo::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (!organizationIds.isEmpty()) {
            organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds).stream().collect(Collectors.toMap(OrganizationDO::getId, d -> d));
        }

        Map<Long, OrganizationDO> finalOrganizationMap = organizationMap;

        Page<InviteCodeDO> page = inviteCodeDao.findByOperatorIdInAndTypeAndDeletedFalse(new ArrayList<>(accountMap.keySet()), InviteCodeType.INVITE, pageable);

        return new PageVO<>(page.getTotalElements(), page.getContent().stream().map(data -> {
            IAccountDao.AccountUserInfo accountInfo = accountMap.get(data.getOperatorId());
            OperatorVO res = new OperatorVO();
            res.setId(data.getId());
            res.setAccountId(data.getOperatorId());
            res.setUsername(accountInfo.getUsername());
            res.setPhone(accountInfo.getPhone());
            if (finalOrganizationMap.containsKey(accountInfo.getOrganizationId())) {
                OrganizationDO organization = finalOrganizationMap.get(accountInfo.getOrganizationId());
                res.setType(organization.getType());
                res.setEnterName(organization.getEnterName());
            }
            res.setInviteCode(data.getInviteCode());
            return res;
        }).collect(Collectors.toList()));
    }

    @Override
    public PageVO<OperatorInviteVO> inviteList(GetOperatorForm form, Pageable pageable) {
        InviteCodeDO inviteData = inviteCodeDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(inviteData), InviteCodeExceptionEnum.OPERATOR_NOT_EXIST);
        List<InviteRecordDO> recordList = inviteRecordDao.findRecordByInviteCodeAndDeletedFalse(inviteData.getInviteCode());
        if (CollectionUtils.isEmpty(recordList)) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        List<Long> accountIds = recordList.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
        Page<IAccountDao.AccountUserInfo> accountInfo = accountDao.getAccountInfo(accountIds, pageable);
        if (CollectionUtils.isEmpty(accountInfo.getContent())) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        Map<Long, OrganizationDO> organizationMap = new HashMap<>();
        List<Long> organizationIds = accountInfo.getContent().stream().map(IAccountDao.AccountUserInfo::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (!organizationIds.isEmpty()) {
            organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds).stream().collect(Collectors.toMap(OrganizationDO::getId, d -> d));
        }

        Map<Long, OrganizationDO> finalOrganizationMap = organizationMap;
        Map<Long, List<VipOrderDO>> orderMap = vipOrderDao.findByCreatorIdInAndDeletedFalse(accountIds).stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));

        return new PageVO<>(accountInfo.getTotalElements(), accountInfo.getContent().stream().map(data -> {
            OperatorInviteVO res = new OperatorInviteVO();
            res.setUsername(data.getUsername());
            res.setPhone(data.getPhone());
            if (finalOrganizationMap.containsKey(data.getOrganizationId())) {
                OrganizationDO organization = finalOrganizationMap.get(data.getOrganizationId());
                res.setType(organization.getType());
                res.setEnterName(organization.getEnterName());
            }
            if (orderMap.containsKey(data.getAccountId())) {
                List<UserOrderDTO> orderList = orderMap.get(data.getAccountId()).stream().map(order -> {
                    UserOrderDTO userOrder = new UserOrderDTO();
                    BeanUtils.copyProperties(order, userOrder);
                    return userOrder;
                }).collect(Collectors.toList());
                res.setOrderList(orderList);
            }
            return res;
        }).collect(Collectors.toList()));
    }

    @Override
    public List<OperatorStatisticsVO> getStatistics(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
        if (CollectionUtils.isEmpty(inviteCodes)) {
            return Collections.emptyList();
        }

        InviteCodeDO inviteCode = inviteCodes.get(0);
        List<InviteRecordDO> recordList = inviteRecordDao.findRecordByInviteCodeAndDeletedFalse(inviteCode.getInviteCode());
        if (CollectionUtils.isEmpty(recordList)) {
            return Collections.emptyList();
        }

        List<Long> accountIds = recordList.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
        List<IAccountDao.AccountUserInfo> accountInfo = accountDao.getAccountInfo(accountIds);
        if (CollectionUtils.isEmpty(accountInfo)) {
            return Collections.emptyList();
        }

        Map<Long, List<VipOrderDO>> orderMap = vipOrderDao.findByCreatorIdInAndDeletedFalse(accountIds).stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));

        return accountInfo.stream().sorted(Comparator.comparing(IAccountDao.AccountUserInfo::getAccountId).reversed())
                .map(data -> {
                    OperatorStatisticsVO res = new OperatorStatisticsVO();
                    res.setUsername(data.getUsername());
                    res.setPhone(data.getPhone());
                    res.setHasOrder(orderMap.containsKey(data.getAccountId()));
                    if (orderMap.containsKey(data.getAccountId())) {
                        List<UserOrderVO> orderList = orderMap.get(data.getAccountId()).stream()
                                .sorted(Comparator.comparing(VipOrderDO::getId).reversed()).map(order -> {
                                    UserOrderVO userOrder = new UserOrderVO();
                                    BeanUtils.copyProperties(order, userOrder);
                                    return userOrder;
                                }).collect(Collectors.toList());
                        res.setOrderList(orderList);
                    }
                    return res;
                }).collect(Collectors.toList());
    }

    @Override
    public OperatorInviteStatisticsVO getInviteStatistics(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        //加入discount_code表的数据
        UserInfoDO userInfo = userInfoDao.findByIdAndDeletedFalse(account.getUserId());
        DiscountCodeDO discountCode = discountCodeDao.findDiscountCodeByAccountInfoAndIsValidAndDeletedFalse(userInfo.getPhone(), true);
        List<VipOrderDO> discountOrderInfos = new ArrayList<>();
        List<IAccountDao.AccountUserInfo> accountInfoList = new ArrayList<>();
        if (discountCode != null) {
            discountOrderInfos = vipOrderDao.findOrderInfosByPayStateAndDiscountCodeIdAndDeletedFalse(PayState.SUCCESS, discountCode.getId());
            List<Long> discountAccountIds = discountOrderInfos.stream().map(VipOrderDO::getCreatorId).distinct().collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(discountOrderInfos)) {
                accountInfoList = accountDao.getAccountInfo(discountAccountIds);
            }
        }
        Map<Long, List<VipOrderDO>> discountOrderMap = discountOrderInfos.stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));

        List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
        OperatorInviteStatisticsVO res = new OperatorInviteStatisticsVO();
        AtomicReference<Double> point = new AtomicReference<>(0.0);
        res.setMembershipRank(membershipRankService.membershipRankList());
        res.setPoint(point.get());
        res.setList(Collections.emptyList());
        if (CollectionUtils.isEmpty(inviteCodes) && CollectionUtils.isEmpty(discountOrderInfos)) {
            return res;
        }

        InviteCodeDO inviteCode = inviteCodes.get(0);
        List<InviteRecordDO> recordList = inviteRecordDao.findRecordByInviteCodeAndDeletedFalse(inviteCode.getInviteCode());
        if (CollectionUtils.isEmpty(recordList) && CollectionUtils.isEmpty(discountOrderInfos)) {
            return res;
        }

        List<Long> accountIds = recordList.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
        List<IAccountDao.AccountUserInfo> accountInfo = accountDao.getAccountInfo(accountIds);
        if (CollectionUtils.isEmpty(accountInfo) && CollectionUtils.isEmpty(discountOrderInfos)) {
            return res;
        }

        // Map<Long, List<VipOrderDO>> orderMap = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayState(accountIds, PayState.SUCCESS).stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));
        Map<Long, List<VipOrderDO>> orderMap;
        if (!CollectionUtils.isEmpty(accountInfo)) {
            orderMap = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayStateAndDiscountCodeIdIsNull(accountIds,
                    PayState.SUCCESS).stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));
        } else {
            orderMap = new HashMap<>(8);
        }
        List<OperatorInviteStatisticsItemVO> list = accountInfo.stream().sorted(Comparator.comparing(IAccountDao.AccountUserInfo::getAccountId).reversed())
                .map(data -> {
                    OperatorInviteStatisticsItemVO item = new OperatorInviteStatisticsItemVO();
                    item.setUsername(data.getUsername());
                    item.setRegisterDate(DateUtil.format(data.getCreateTime(), "yyyy-MM-dd"));
                    if (orderMap.get(data.getAccountId()) == null) {
                        item.setOrderSuccessCount(0);
                    } else {
                        item.setOrderSuccessCount(orderMap.get(data.getAccountId()).size());
                    }
                    //   point.updateAndGet(v -> v + (item.getOrderSuccessCount() * account.getPointScale()));
                    return item;
                }).collect(Collectors.toList());

        for (IAccountDao.AccountUserInfo accountUserInfo : accountInfoList) {
            OperatorInviteStatisticsItemVO operatorItem = new OperatorInviteStatisticsItemVO();
            operatorItem.setUsername(accountUserInfo.getUsername());
            operatorItem.setRegisterDate(DateUtil.format(accountUserInfo.getCreateTime(), "yyyy-MM-dd"));
            if (discountOrderMap.get(accountUserInfo.getAccountId()) == null) {
                operatorItem.setOrderSuccessCount(0);
            } else {
                operatorItem.setOrderSuccessCount(orderMap.get(accountUserInfo.getAccountId()).size());
            }
            list.add(operatorItem);
            //  point.updateAndGet(v -> v + (operatorItem.getOrderSuccessCount() * discountCode.getPointScale()));
        }

        res.setHistoryPoint(account.getHistoryScore());
        List<ExtractPointDO> extractPointDOList = extractPointDao.findExtractedPointByDeletedFalse(accountId, account.getOrigin());
        AtomicReference<Double> extratedPoint = new AtomicReference<>(0.0);
        extractPointDOList.forEach(extractPoint -> {
            extratedPoint.updateAndGet(v -> v + extractPoint.getPoint());
        });
        res.setPoint(account.getHistoryScore() == null ? 0 : account.getHistoryScore() - extratedPoint.get());
        res.setList(list);
        return res;
    }

    @Override
    public Boolean downloadStatistics(HttpServletResponse response, Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        List<OperatorStatisticsExcelVO> exportList = new ArrayList<>();
        List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
        if (!CollectionUtils.isEmpty(inviteCodes)) {
            InviteCodeDO inviteCode = inviteCodes.get(0);
            List<InviteRecordDO> recordList = inviteRecordDao.findRecordByInviteCodeAndDeletedFalse(inviteCode.getInviteCode());
            if (!CollectionUtils.isEmpty(recordList)) {
                List<Long> accountIds = recordList.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
                List<IAccountDao.AccountUserInfo> accountInfo = accountDao.getAccountInfo(accountIds);
                if (!CollectionUtils.isEmpty(accountInfo)) {
                    Map<Long, List<VipOrderDO>> orderMap = vipOrderDao.findByCreatorIdInAndDeletedFalse(accountIds).stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));

                    accountInfo.stream().sorted(Comparator.comparing(IAccountDao.AccountUserInfo::getAccountId).reversed())
                            .forEach(data -> {
                                if (orderMap.containsKey(data.getAccountId())) {
                                    exportList.addAll(orderMap.get(data.getAccountId()).stream().map(order -> {
                                        OperatorStatisticsExcelVO res = new OperatorStatisticsExcelVO();
                                        res.setUsername(data.getUsername());
                                        res.setPhone(data.getPhone());
                                        res.setVipName(order.getVipName());
                                        res.setTotalPrice(order.getTotalPrice());
                                        res.setPayTime(new Date(order.getPayTime().getTime()));
                                        res.setPayState(PayState.SUCCESS.equals(order.getPayState()) ? "已支付" : "未支付");
                                        return res;
                                    }).collect(Collectors.toList()));
                                } else {
                                    OperatorStatisticsExcelVO res = new OperatorStatisticsExcelVO();
                                    res.setUsername(data.getUsername());
                                    res.setPhone(data.getPhone());
                                    exportList.add(res);
                                }
                            });
                }
            }
        }

        //加入discount_code表中的数据
        String phone = userInfoDao.findByIdAndDeletedFalse(account.getUserId()).getPhone();
        DiscountCodeDO discountCode = discountCodeDao.findDiscountCodeByAccountInfoAndIsValidAndDeletedFalse(phone, true);
        List<VipOrderDO> discountOrderInfos = new ArrayList<>();
        if (discountCode != null) {
            discountOrderInfos = vipOrderDao.findOrderInfosByDiscountCodeIdAndDeletedFalse(discountCode.getId());
        }
        Map<Long, List<VipOrderDO>> discountOrderMap = discountOrderInfos.stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));
        List<Long> discountAccountIds = discountOrderInfos.stream().map(VipOrderDO::getCreatorId).distinct().collect(Collectors.toList());
        List<IAccountDao.AccountUserInfo> accountInfoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(discountOrderInfos)) {
            accountInfoList = accountDao.getAccountInfo(discountAccountIds);
        }
        accountInfoList.forEach(data -> {
            if (discountOrderMap.containsKey(data.getAccountId())) {
                exportList.addAll(discountOrderMap.get(data.getAccountId()).stream().map(order -> {
                    OperatorStatisticsExcelVO res = new OperatorStatisticsExcelVO();
                    res.setUsername(data.getUsername());
                    res.setPhone(data.getPhone());
                    res.setVipName(order.getVipName());
                    res.setTotalPrice(order.getTotalPrice());
                    res.setPayTime(new Date(order.getPayTime().getTime()));
                    res.setPayState(PayState.SUCCESS.equals(order.getPayState()) ? "已支付" : "未支付");
                    return res;
                }).collect(Collectors.toList()));
            } else {
                OperatorStatisticsExcelVO res = new OperatorStatisticsExcelVO();
                res.setUsername(data.getUsername());
                res.setPhone(data.getPhone());
                exportList.add(res);
            }
        });

        try {
            ExcelUtil.exportExcel("数据统计", "统计信息", OperatorStatisticsExcelVO.class, exportList, "统计信息表", response);
        } catch (Exception e) {
            log.error("用户【" + accountId + "】运营商数据统计导出失败：", e);
            throw new ServiceException(SysCode.FILE_EXPORT_ERROR);
        }

        return true;
    }

    @Override
    public Boolean extractPoint(ExtractPointForm form, Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        AssertUtil.assertTrue(form.getPoint() > getInviteStatistics(accountId).getPoint(), AccountExceptionEnum.NOT_ENOUGH_POINT);
        ExtractPointDO extractPointDO = new ExtractPointDO();
        extractPointDO.setPoint(form.getPoint());
        extractPointDO.setCreatorId(accountId);
        extractPointDO.setTarget(form.getTarget());
        extractPointDO.setExtractPointState(ExtractPointState.EXTRACTING);
        extractPointDO.setType(form.getType());
        extractPointDao.save(extractPointDO);
        return Boolean.TRUE;
    }

    @Override
    public PageVO<ExtractPointRecordVO> getExtractPointRecord(Long accountId, Pageable pageable) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        Page<ExtractPointDO> page = extractPointDao.findAllByCreatorIdAndDeletedFalseOrderByCreateTimeDesc(accountId, pageable);
        return new PageVO<>(page.getTotalElements(), page.getContent().
                stream().map(data -> {
                    ExtractPointRecordVO res = new ExtractPointRecordVO();
                    res.setPoint(data.getPoint());
                    res.setExtractPointState(data.getExtractPointState());
                    res.setCreateTime(DateUtil.format(data.getCreateTime(), "yyyy-MM-dd HH:mm"));
                    return res;
                }).collect(Collectors.toList()));

    }

    @Override
    public PageVO<InviteOrderDetailVO> getInviteOrderDetail(Long accountId, Integer pageNum, Integer pageSize) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);

        //加入discount_code表中的数据
        UserInfoDO userInfo = userInfoDao.findByIdAndDeletedFalse(account.getUserId());
        DiscountCodeDO discountCode = discountCodeDao.findDiscountCodeByAccountInfoAndIsValidAndDeletedFalse(userInfo.getPhone(), true);
        List<VipOrderDO> discountOrderInfos = new ArrayList<>();
        if (discountCode != null) {
            discountOrderInfos = vipOrderDao.findOrderInfosByPayStateAndDiscountCodeIdAndDeletedFalse(PayState.SUCCESS, discountCode.getId());
        }

        List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
        if (CollectionUtils.isEmpty(inviteCodes) && CollectionUtils.isEmpty(discountOrderInfos)) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        InviteCodeDO inviteCode = inviteCodes.get(0);
        List<InviteRecordDO> recordList = inviteRecordDao.findRecordByInviteCodeAndDeletedFalse(inviteCode.getInviteCode());
        if (CollectionUtils.isEmpty(recordList) && CollectionUtils.isEmpty(discountOrderInfos)) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        List<Long> accountIds = recordList.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
        List<IAccountDao.AccountUserInfo> accountInfo = accountDao.getAccountInfo(accountIds);
        if (CollectionUtils.isEmpty(accountInfo) && CollectionUtils.isEmpty(discountOrderInfos)) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        List<VipOrderDO> orders = new LinkedList<>();
        if (!CollectionUtils.isEmpty(accountIds)) {
            orders = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayStateAndDiscountCodeIdIsNull(accountIds, PayState.SUCCESS);
        }
        orders.addAll(discountOrderInfos);
        orders = orders.stream().sorted(Comparator.comparing(VipOrderDO::getCreateTime).reversed()).collect(Collectors.toList());
        List<OrderHistoryScoreVO> orderHistoryScoreInfos = new ArrayList<>();
        for (VipOrderDO order : orders) {
            OrderHistoryScoreVO orderHistoryScore = new OrderHistoryScoreVO();
            orderHistoryScore.setOrderId(order.getId());
            orderHistoryScore.setCreatorId(order.getCreatorId());
            orderHistoryScore.setHistoryScore(order.getHistoryScore());
            orderHistoryScore.setCreateTime(order.getCreateTime());
            orderHistoryScoreInfos.add(orderHistoryScore);
        }
        //查询下下级分享记录
        List<String> downInviteCodes = inviteCodeDao.findInfosByOperatorIdInAndTypeAndDeletedFalse(accountIds, InviteCodeType.INVITE).stream().map(InviteCodeDO::getInviteCode).collect(Collectors.toList());
        List<InviteRecordDO> downRecords = inviteRecordDao.findRecordByInviteCodeInAndDeletedFalse(downInviteCodes).stream().
                filter(s -> s.getIsPay() != null && s.getIsPay()).collect(Collectors.toList());
        List<Long> downAccountIds = downRecords.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
        List<VipOrderDO> downOrderInfos = vipOrderDao.findOrderInfosByDiscountCodeIdInAndPayStateAndDeletedFalse(downAccountIds, PayState.SUCCESS);

        for (VipOrderDO downOrderInfo : downOrderInfos) {
            OrderHistoryScoreVO orderHistoryScore = new OrderHistoryScoreVO();
            orderHistoryScore.setOrderId(downOrderInfo.getId());
            orderHistoryScore.setCreateTime(downOrderInfo.getCreateTime());
            orderHistoryScore.setCreatorId(downOrderInfo.getCreatorId());
            orderHistoryScore.setHistoryScore(Double.valueOf(String.format("%.2f", downOrderInfo.getTotalPrice() * 0.1)));
            orderHistoryScoreInfos.add(orderHistoryScore);
        }

        Long sum = (long) orderHistoryScoreInfos.size();
        orderHistoryScoreInfos = PageUtil.Page(orderHistoryScoreInfos, pageNum, pageSize);
        return new PageVO<>(sum, orderHistoryScoreInfos.
                stream().map(data -> {
                    InviteOrderDetailVO res = new InviteOrderDetailVO();
                    res.setPoint(data.getHistoryScore() == null ? 0 : data.getHistoryScore());
                    res.setName(accountDao.findByIdAndDeletedFalse(data.getCreatorId()).getUsername());
                    res.setHappenTime(DateUtil.format(data.getCreateTime(), "yyyy-MM-dd HH:mm"));
                    return res;
                }).collect(Collectors.toList()));
    }

    @Override
    public PageVO<DistributeInfoVO> distributeInfoList(DistributeQueryVO query, Pageable pageable) {

        Page<InviteCodeDO> pageInfos = inviteCodeDao.findInfosByInviteCodeAndDeletedFalseForQuery(query.getDistributeCode(), pageable);
        List<InviteCodeDO> content = pageInfos.getContent();
        List<String> inviteCodes = content.stream().map(InviteCodeDO::getInviteCode).collect(Collectors.toList());
        Map<String, Integer> registerMap = new HashMap<>(8);
        Map<String, Integer> useNumMap = new HashMap<>(8);
        Map<String, Integer> useTimesMap = new HashMap<>(8);
        if (!CollectionUtils.isEmpty(inviteCodes)) {
            List<InviteRecordDO> inviteRecordInfos = inviteRecordDao.findRecordByInviteCodeInAndDeletedFalse(inviteCodes);
            Map<String, List<InviteRecordDO>> inviteCodeMap = inviteRecordInfos.stream().collect(Collectors.groupingBy(InviteRecordDO::getInviteCode));
            List<Long> registerIds = inviteRecordInfos.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
            List<VipOrderDO> orderInfos = new ArrayList<>();
            if (!CollectionUtils.isEmpty(registerIds)) {
                orderInfos = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayState(registerIds, PayState.SUCCESS);
            }
            List<VipOrderDO> aliOrders = orderInfos.stream().filter(s -> s.getPrepayId() == null).collect(Collectors.toList());
            List<VipOrderDO> wxOrders = orderInfos.stream().filter(s -> s.getPrepayId() != null).collect(Collectors.toList());
            List<String> tradeNoInfos = aliOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(tradeNoInfos)) {
                List<QueryRecordDO> queryRecordInfos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNoInfos);
                tradeNoInfos = queryRecordInfos.stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
            }
            List<String> finalTradeNoInfos = tradeNoInfos;
            aliOrders = aliOrders.stream().filter(s -> finalTradeNoInfos.contains(s.getTradeNo())).collect(Collectors.toList());
            wxOrders.addAll(aliOrders);
            Map<Long, List<VipOrderDO>> orderInfoMap = wxOrders.stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));
            for (String inviteCode : inviteCodes) {
                List<InviteRecordDO> inviteRecordList = inviteCodeMap.getOrDefault(inviteCode, new ArrayList<>());
                List<VipOrderDO> orderList = new ArrayList<>();
                for (InviteRecordDO inviteRecord : inviteRecordList) {
                    Long creatorId = inviteRecord.getCreatorId();
                    List<VipOrderDO> vipOrderList = orderInfoMap.getOrDefault(creatorId, new ArrayList<>());
                    orderList.addAll(vipOrderList);
                }
                if (query.getStartTime() != null || query.getEndTime() != null) {
                    inviteRecordList = inviteRecordList.stream().filter(s -> (query.getStartTime() == null || s.getCreateTime().after(query.getStartTime()))
                            && (query.getEndTime() == null || s.getCreateTime().before(query.getEndTime()))).collect(Collectors.toList());
                    orderList = orderList.stream().filter(s -> (query.getStartTime() == null || s.getCreateTime().after(query.getStartTime()))
                            && (query.getEndTime() == null || s.getCreateTime().before(query.getEndTime()))).collect(Collectors.toList());
                }
                registerMap.put(inviteCode, inviteRecordList.size());
                useNumMap.put(inviteCode, orderList.stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId)).keySet().size());
                useTimesMap.put(inviteCode, orderList.size());
            }
        }

        List<Long> operatorIds = content.stream().map(InviteCodeDO::getOperatorId).collect(Collectors.toList());
        Map<Long, IAccountDao.AccountUserInfo> accountMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(operatorIds)) {
            accountMap = accountDao.getAccountInfo(operatorIds).stream().
                    collect(Collectors.toMap(IAccountDao.AccountUserInfo::getAccountId, Function.identity()));
        }
        Map<Long, MembershipRankDO> rankMap = membershipRankDao.findInfos().stream().collect(Collectors.
                toMap(MembershipRankDO::getId, Function.identity()));
        List<DistributeInfoVO> result = new ArrayList<>();
        for (InviteCodeDO inviteCode : content) {
            DistributeInfoVO distributeInfo = new DistributeInfoVO();
            distributeInfo.setDistributeId(inviteCode.getId());
            distributeInfo.setDistributeCode(inviteCode.getInviteCode());
            Long operatorId = inviteCode.getOperatorId();
            IAccountDao.AccountUserInfo accountUserInfo = accountMap.get(operatorId);
            if (accountUserInfo != null) {
                distributeInfo.setAccountInfo(accountUserInfo.getPhone());
                distributeInfo.setOwner(accountUserInfo.getUsername());
                Long membershipRankId = accountUserInfo.getMembershipRankId();
                MembershipRankDO membershipRank = rankMap.get(membershipRankId);
                distributeInfo.setRank(membershipRank.getMembershipRank());
            }
            distributeInfo.setRegisterNum(registerMap.getOrDefault(inviteCode.getInviteCode(), 0));
            distributeInfo.setUseNum(useNumMap.getOrDefault(inviteCode.getInviteCode(), 0));
            distributeInfo.setUseTimes(useTimesMap.getOrDefault(inviteCode.getInviteCode(), 0));
            result.add(distributeInfo);
        }
        return new PageVO<>(pageInfos.getTotalElements(), result);
    }

    @Override
    public List<DistributeCodeUseDetailVO> distributeDetailInfos(Long distributeId) {

        InviteCodeDO inviteCodeInfo = inviteCodeDao.findByIdAndDeletedFalse(distributeId);
        AssertUtil.assertNotNull(inviteCodeInfo, DisCountCodeExceptionEnum.DISTRIBUTE_CODE_NOT_EXIST);
        String inviteCode = inviteCodeInfo.getInviteCode();
        List<InviteRecordDO> inviteRecordInfos = inviteRecordDao.findRecordByInviteCodeInAndDeletedFalse(Collections.singletonList(inviteCode));
        List<Long> registerIds = inviteRecordInfos.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
        List<VipOrderDO> orderInfos = new ArrayList<>();
        if (!CollectionUtils.isEmpty(registerIds)) {
            orderInfos = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayState(registerIds, PayState.SUCCESS);
        }
        List<VipOrderDO> aliOrders = orderInfos.stream().filter(s -> s.getPrepayId() == null).collect(Collectors.toList());
        List<VipOrderDO> wxOrders = orderInfos.stream().filter(s -> s.getPrepayId() != null).collect(Collectors.toList());
        List<String> tradeNoInfos = aliOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(tradeNoInfos)) {
            List<QueryRecordDO> queryRecordInfos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNoInfos);
            tradeNoInfos = queryRecordInfos.stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
        }
        List<String> finalTradeNoInfos = tradeNoInfos;
        aliOrders = aliOrders.stream().filter(s -> finalTradeNoInfos.contains(s.getTradeNo())).collect(Collectors.toList());
        wxOrders.addAll(aliOrders);
        List<Long> payIds = wxOrders.stream().map(VipOrderDO::getCreatorId).distinct().collect(Collectors.toList());
        Map<Long, IAccountDao.AccountUserInfo> accountMap = accountDao.getAccountInfo(payIds).stream().collect(Collectors.
                toMap(IAccountDao.AccountUserInfo::getAccountId, Function.identity()));
        List<DistributeCodeUseDetailVO> result = new ArrayList<>();
        int orderNum = 1;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        for (VipOrderDO order : wxOrders) {
            DistributeCodeUseDetailVO distributeCodeUseDetail = new DistributeCodeUseDetailVO();
            distributeCodeUseDetail.setOrderNum(orderNum++);
            Long creatorId = order.getCreatorId();
            IAccountDao.AccountUserInfo accountUserInfo = accountMap.get(creatorId);
            distributeCodeUseDetail.setAccountInfo(accountUserInfo == null ? null : accountUserInfo.getPhone());
            distributeCodeUseDetail.setUserName(accountUserInfo == null ? null : accountUserInfo.getUsername());
            distributeCodeUseDetail.setDiscountCode(inviteCode);
            distributeCodeUseDetail.setUseDate(dateFormat.format(order.getPayTime()));
            result.add(distributeCodeUseDetail);
        }
        return result;
    }

    @Override
    public void distributeDetailInfosExport(HttpServletResponse response, Long distributeId) {
        List<DistributeCodeUseDetailVO> distributeCodeUseDetails = distributeDetailInfos(distributeId);
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("分享码使用明细", "sheet1", ExcelType.XSSF),
                DistributeCodeUseDetailVO.class, distributeCodeUseDetails);
        try {
            ExcelUtil.downLoadExcel("分享码使用明细", response, workbook);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void updateHistoryScore(VipOrderDO vipOrderDO) {
        Double addScore = null;
        AccountDO accountDO = null;
        //首先判断该订单是否是推荐码,如果是推荐码的订单，积分归于推荐码所属的账号，且推荐码有自己的积分比例
        if (vipOrderDO.getDiscountCodeId() != null) {
            DiscountCodeDO discountCodeDO = discountCodeDao.findDiscountCodeByIdAndDeletedFalse(vipOrderDO.getDiscountCodeId());
            if (discountCodeDO.getAccountInfo() != null) {
                addScore = discountCodeDO.getPointScale();
                accountDO = accountDao.findByIdAndDeletedFalse(Long.parseLong(discountCodeDO.getAccountInfo()));
            }
        } else {
            AccountDO accountPay = accountDao.findByIdAndDeletedFalse(vipOrderDO.getCreatorId());
            List<InviteRecordDO> inviteRecordDO = inviteRecordDao.findRecordByCreatorIdAndTypeAndDeletedFalse(accountPay.getId(), InviteCodeType.INVITE);
            if (!inviteRecordDO.isEmpty()) {
                accountDO = accountDao.findByIdAndDeletedFalse(
                        inviteCodeDao.findCodeByInviteCodeAndDeletedFalse(inviteRecordDO.get(0).getInviteCode()).getOperatorId()
                );
                MembershipRankDO membershipRankDO = membershipRankDao.findByIdAndDeletedFalse(accountDO.getMembershipRankId());
                addScore = membershipRankDO.getScore();
            }
        }
        //再判断是否订单所属账号是否是被邀请的，如果是被邀请的，积分归于邀请账户，积分比例根据邀请账户会员等级来计算
        //推荐码和邀请码产生的积分都算作积分总分，都可以用来提升会员等级
        if (accountDO != null && addScore != null) {
            vipOrderDO.setHistoryScore(addScore);
            vipOrderDao.save(vipOrderDO);
            Double totalHistoryScore = accountDO.getHistoryScore() + addScore;
            MembershipRankDO rankOption = membershipRankDao.findMembershipRankDOByValue(totalHistoryScore);
            accountDO.setMembershipRankId(rankOption.getId());
            accountDO.setHistoryScore(totalHistoryScore);
            accountDao.save(accountDO);
        }
    }

    @Override
    public void updateUserHistoryScore() {
        List<AccountDO> allInfos = accountDao.findAllInfos();
        for (AccountDO allInfo : allInfos) {
            Double historyScore = 0.0;
            EndpointType origin = allInfo.getOrigin();
            //查询用户有关分享码和推荐码订单信息
            List<InviteCodeDO> inviteCodeDOList = inviteCodeDao.findInfosByOperatorIdInAndTypeAndDeletedFalse(Collections.singletonList(allInfo.getId()),
                    InviteCodeType.INVITE);
            InviteCodeDO inviteCode = inviteCodeDOList.isEmpty() ? null : inviteCodeDOList.get(0);
            List<VipOrderDO> inviteOrders = new ArrayList<>();
            if (inviteCode != null) {
                List<Long> inviteUseIds = inviteRecordDao.findRecordByInviteCodeInAndDeletedFalse(Collections.
                        singletonList(inviteCode.getInviteCode())).stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
                inviteOrders = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayState(inviteUseIds, PayState.SUCCESS);
            }
            List<DiscountCodeDO> discountCodeDOList = discountCodeDao.findInfosByAccountInfoInAndIsValidAndDeletedFalse(Collections.
                    singletonList(allInfo.getId().toString()), Boolean.TRUE);
            DiscountCodeDO discountCode = discountCodeDOList.isEmpty() ? null : discountCodeDOList.get(0);
            List<VipOrderDO> discountOrders = new ArrayList<>();
            if (discountCode != null) {
                discountOrders = vipOrderDao.findOrderInfosByDiscountCodeIdInAndPayStateAndDeletedFalse(Collections.
                        singletonList(discountCode.getId()), PayState.SUCCESS);
            }
            inviteOrders.addAll(discountOrders);
            inviteOrders = inviteOrders.stream().filter(s -> s.getVipId() == 8L || s.getVipId() == 13L ||
                    s.getVipId() == 14L).collect(Collectors.toList());
            if (origin != null && origin.equals(EndpointType.ALIPAY_APPLET)) {
                List<String> tradeNos = inviteOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
                tradeNos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNos).
                        stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
                List<String> finalTradeNos = tradeNos;
                inviteOrders = inviteOrders.stream().filter(s -> finalTradeNos.contains(s.getTradeNo())).collect(Collectors.toList());
            }
            for (VipOrderDO order : inviteOrders) {
                historyScore += order.getHistoryScore();
            }
            allInfo.setHistoryScore(historyScore);
            accountDao.save(allInfo);
        }
    }

    @Override
    public void distributeInfosExport(HttpServletResponse response) {
        List<String> inviteCodes = inviteRecordDao.findTOP100InviteCode();
        List<InviteCodeDO> inviteCodeInfos = inviteCodeDao.findInfosByInviteCodeInAndDeletedFalse(inviteCodes);
        List<Long> operatorIds = inviteCodeInfos.stream().map(InviteCodeDO::getOperatorId).collect(Collectors.toList());
        Map<String, Integer> registerMap = new HashMap<>(8);
        Map<String, Integer> useNumMap = new HashMap<>(8);
        Map<String, Integer> useTimesMap = new HashMap<>(8);
        if (!CollectionUtils.isEmpty(inviteCodes)) {
            List<InviteRecordDO> inviteRecordInfos = inviteRecordDao.findRecordByInviteCodeInAndDeletedFalse(inviteCodes);
            Map<String, List<InviteRecordDO>> inviteCodeMap = inviteRecordInfos.stream().collect(Collectors.groupingBy(InviteRecordDO::getInviteCode));
            List<Long> registerIds = inviteRecordInfos.stream().map(InviteRecordDO::getCreatorId).collect(Collectors.toList());
            List<VipOrderDO> orderInfos = new ArrayList<>();
            if (!CollectionUtils.isEmpty(registerIds)) {
                orderInfos = vipOrderDao.findByCreatorIdInAndDeletedFalseAndPayState(registerIds, PayState.SUCCESS);
            }
            List<VipOrderDO> aliOrders = orderInfos.stream().filter(s -> s.getPrepayId() == null).collect(Collectors.toList());
            List<VipOrderDO> wxOrders = orderInfos.stream().filter(s -> s.getPrepayId() != null).collect(Collectors.toList());
            List<String> tradeNoInfos = aliOrders.stream().map(VipOrderDO::getTradeNo).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(tradeNoInfos)) {
                List<QueryRecordDO> queryRecordInfos = queryRecordDao.findByAuthorizedResultAndTradeNoIn(AuthResultType.AUTHORIZED, tradeNoInfos);
                tradeNoInfos = queryRecordInfos.stream().map(QueryRecordDO::getTradeNo).collect(Collectors.toList());
            }
            List<String> finalTradeNoInfos = tradeNoInfos;
            aliOrders = aliOrders.stream().filter(s -> finalTradeNoInfos.contains(s.getTradeNo())).collect(Collectors.toList());
            wxOrders.addAll(aliOrders);
            Map<Long, List<VipOrderDO>> orderInfoMap = wxOrders.stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId));
            for (String inviteCode : inviteCodes) {
                List<InviteRecordDO> inviteRecordList = inviteCodeMap.getOrDefault(inviteCode, new ArrayList<>());
                List<VipOrderDO> orderList = new ArrayList<>();
                for (InviteRecordDO inviteRecord : inviteRecordList) {
                    Long creatorId = inviteRecord.getCreatorId();
                    List<VipOrderDO> vipOrderList = orderInfoMap.getOrDefault(creatorId, new ArrayList<>());
                    orderList.addAll(vipOrderList);
                }

                registerMap.put(inviteCode, inviteRecordList.size());
                useNumMap.put(inviteCode, orderList.stream().collect(Collectors.groupingBy(VipOrderDO::getCreatorId)).keySet().size());
                useTimesMap.put(inviteCode, orderList.size());
            }
        }
        Map<Long, IAccountDao.AccountUserInfo> accountMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(operatorIds)) {
            accountMap = accountDao.getAccountInfo(operatorIds).stream().
                    collect(Collectors.toMap(IAccountDao.AccountUserInfo::getAccountId, Function.identity()));
        }
        Map<Long, MembershipRankDO> rankMap = membershipRankDao.findInfos().stream().collect(Collectors.
                toMap(MembershipRankDO::getId, Function.identity()));
        List<DistributeInfoVO> result = new ArrayList<>();
        for (InviteCodeDO inviteCode : inviteCodeInfos) {
            DistributeInfoVO distributeInfo = new DistributeInfoVO();
            distributeInfo.setDistributeId(inviteCode.getId());
            distributeInfo.setDistributeCode(inviteCode.getInviteCode());
            Long operatorId = inviteCode.getOperatorId();
            IAccountDao.AccountUserInfo accountUserInfo = accountMap.get(operatorId);
            if (accountUserInfo != null) {
                distributeInfo.setAccountInfo(accountUserInfo.getPhone());
                distributeInfo.setOwner(accountUserInfo.getUsername());
                Long membershipRankId = accountUserInfo.getMembershipRankId();
                MembershipRankDO membershipRank = rankMap.get(membershipRankId);
                distributeInfo.setRank(membershipRank.getMembershipRank());
            }
            distributeInfo.setRegisterNum(registerMap.getOrDefault(inviteCode.getInviteCode(), 0));
            distributeInfo.setUseNum(useNumMap.getOrDefault(inviteCode.getInviteCode(), 0));
            distributeInfo.setUseTimes(useTimesMap.getOrDefault(inviteCode.getInviteCode(), 0));
            result.add(distributeInfo);
        }
        result = result.stream().sorted(Comparator.comparing(DistributeInfoVO::getRegisterNum, Comparator.reverseOrder())).
                collect(Collectors.toList());
        try {
            ExcelUtil.exportExcel("分享码信息", "分享码信息", DistributeInfoVO.class, result, "", response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
